package com.ctsi.framework.common.config;

import com.ctsi.framework.common.exception.ExceptionFactory;
import com.ctsi.framework.rest.api.config.RestApiDispatcherServletConfiguration;
import feign.Feign;
import feign.RequestInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.security.oauth2.client.feign.OAuth2FeignRequestInterceptor;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.*;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author: zhangjw
 * @Date: 2018/9/12 17:09
 * @Description: 根据约定项目目录结构自动配置扫描初始化系统所需的bean，无需在main函数上扫描;
 *  默认全部懒加载
 */
@Configuration
@Import({RestApiDispatcherServletConfiguration.class,JacksonConfiguration.class})
@ComponentScan(basePackages = {"com.ctsi.**.service",//业务
        "com.ctsi.**.api.impl.local",//远程服务本地实现
        "com.ctsi.**.config", //模块属性配置目录
        "com.ctsi.**.manager.impl",
        "com.ctsi.**.manager",
        "com.ctsi.**.event.listener" // 事件监听器
},lazyInit = true)
public class ApplicationAutoConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(ApplicationAutoConfiguration.class);

    /**
     * 加载Feign客户端
     */
    @Configuration
    @ConditionalOnClass({Feign.class,OAuth2ClientContext.class})
    @EnableCircuitBreaker
    public static class FeignClientConfig {

        public FeignClientConfig() {
            logger.info("start init Fegin clients!");
        }

        /**
         * 解决Feign 丢失Auth token的问题
         * @param oAuth2ClientContext
         * @param resource
         * @return
         */
        @Bean
        public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource){
            return new OAuth2FeignRequestInterceptor(oAuth2ClientContext,resource);
        }

    }

    @Bean
    public ExceptionFactory exceptionFactory(MessageSource messageSource){
        return new ExceptionFactory(messageSource);
    }

    @Configuration
    @AutoConfigureBefore(ValidationAutoConfiguration.class)
    public static class ValidationConfig{

        private MessageSource messageSource;
        @Autowired
        public ValidationConfig(MessageSource messageSource) {
            this.messageSource = messageSource;
        }

        @Bean
        public WebMvcConfigurer beanValidator(){
            return new WebMvcConfigurer() {
                /**
                 * Provide a custom {@link Validator} instead of the one created by default.
                 * The default implementation, assuming JSR-303 is on the classpath, is:
                 * {@link OptionalValidatorFactoryBean}.
                 * Leave the return value as {@code null} to keep the default.
                 */
                @Override
                public Validator getValidator() {
                    LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
                    validatorFactoryBean.setValidationMessageSource(messageSource);
                    return validatorFactoryBean;
                }
            };
        }
    }


    /**
     * 初始化密码加密
     */
    @Configuration
    @ConditionalOnClass(PasswordEncoder.class)
    public static class passwordEncoder {

        @Bean
        @Primary
        public PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }

    }

}
